import { isObj, isArr} from './utils.js'
import ArrayProto from './arrProto.js'
import Dep from './watchAndDep/Dep.js'
/**
 * 1. this.value = value
 * 2. this.dep = new Dep
 * 3. def(value, '__ob__', this)
 * 4. Array.isArray(value){
 *     判断当前 数组是否是被修改的数组， 如果不是， 就手动修改为我们的自定义方法的数组
 *      if (hasProto =》 ) {
            protoAugment(value, arrayMethods);
          } else {
            copyAugment(value, arrayMethods, arrayKeys);
          }
 *     this.observeArray(value);
 * }else{
 *     this.walk(value)
 * }
 * */
let hasProto = '__proto__' in ({})

function def (obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
        value: val,
        enumerable: !!enumerable,
        writable: true,
        configurable: true
    });
}

class Observer{
    constructor(value){
        this.value = value
        this.dep = new Dep()
        def(value, '__ob__', this)
        if(isArr(value)){
            if(hasProto){
                value.__proto__ = ArrayProto
            }
            this.observeArray(value);
        }else{
            this.walk(value)
        }
    }
    observeArray(items){
        items.forEach(item => observe(item))
    }
    walk(data){
        for(let i in data){
            defineReactive(data, i, data[i])
        }
    }
}

// full
function defineReactive(data, key, val) {

    let childOb = observe(val)
    let dep = new Dep()
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get(){
            if(Dep.target){
                dep.depend()
                if(childOb){
                    childOb.dep.depend()
                    if(Array.isArray(val)){
                        dependArray(val)
                    }
                }
            }
            return val
        },
        set(newVal){
            if(newVal === val) return
            // console.log('setter')
            val = newVal
            observe(val)
            dep.notify()
        }
    })
}


function dependArray (value) {
    for (let e, i = 0, l = value.length; i < l; i++) {
      e = value[i]
      e && e.__ob__ && e.__ob__.dep.depend()
      if (Array.isArray(e)) {
        dependArray(e)
      }
    }
  }

function arryObserve(data){
    let e = (void 0)
    for(let i=0, l=data.length; i < l ; i++){
        e = data[i];
        e && e.__ob__ && e.__ob__.dep.depend();
        observe(data[i])
    }
}



function observe(data){
    if(!isObj(data) && !isArr(data)) return;
    let ob = null
    if(('__ob__' in data) && data.__ob__ instanceof Observer) {
       ob = data.__ob__
    }else{
       ob = new Observer(data)
    }
    return ob
}

function initData (vm) {
    // new Observer(vm.$data)
    observe(vm.$data)
}
// Firefox has a "watch" function on Object.prototype...
let nativeWatch = ({}).watch;


// watcher 和 computed 的初始化逻辑一致， 提出一个公共方法
function doCallback(vm, alias, cb){
    let options = vm.$options

    if(options[alias] && isObj(options[alias])){
        for(let key in options[alias]){
            cb(key, options[alias][key])
        }
    }
}

function initWatcher(vm){
    doCallback(vm, 'watch', (key , val)=>{
        vm.$watch(key, val)
    })
}


export default function initState (vm) {
    vm._watchers = [];
    let opts = vm.$options;
    if (opts.props) { initProps(vm, opts.props); }
    if (opts.methods) { initMethods(vm, opts.methods); }
    if (vm.$data) {
        initData(vm);
    }
    if (opts.computed) { initComputed(vm, opts.computed); }
    if (opts.watch && opts.watch !== nativeWatch) {
        //initWatch(vm, opts.watch);
        initWatcher(vm)
    }

}
